<!DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js webgl - postprocessing - godrays</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<style>
			body {
				background-color: #000000;
				margin: 0px;
				overflow: hidden;
				font-family:Monospace;
				font-size:13px;
				text-align:center;
				font-weight: bold;
				text-align:center;
			}

			a {
				color:#0078ff;
			}

			#info {
				color:#fff;
				position: absolute;
				top: 0px; width: 100%;
				padding: 5px;
				z-index:100;
			}
		</style>
	</head>

	<body>
		<script src="../build/three.js"></script>
		<script src="js/loaders/OBJLoader.js"></script>
		<script src="js/ShaderGodRays.js"></script>

		<script src="js/Detector.js"></script>
		<script src="js/libs/stats.min.js"></script>

		<div id="info">
			<a href="http://threejs.org" target="_blank">three.js</a> - webgl god-rays example - tree by <a href="http://www.turbosquid.com/3d-models/free-tree-3d-model/592617" target="_blank">stanloshka</a>
		</div>


		<script>

			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();

			var container, stats;
			var camera, scene, renderer, materialDepth;

			var sphereMesh;

			var sunPosition = new THREE.Vector3( 0, 1000, -1000 );
			var screenSpacePosition = new THREE.Vector3();

			var mouseX = 0, mouseY = 0;

			var windowHalfX = window.innerWidth / 2;
			var windowHalfY = window.innerHeight / 2;

			var postprocessing = { enabled : true };

			var orbitRadius = 200;

			var bgColor = 0x000511;
			var sunColor = 0xffee00;

			init();
			animate();

			function init() {

				container = document.createElement( 'div' );
				document.body.appendChild( container );

				//

				camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 3000 );
				camera.position.z = 200;

				scene = new THREE.Scene();

				//

				materialDepth = new THREE.MeshDepthMaterial();

				var materialScene = new THREE.MeshBasicMaterial( { color: 0x000000, shading: THREE.FlatShading } );

				// tree

				var loader = new THREE.OBJLoader();
				loader.load( "models/obj/tree.obj", function ( object ) {

					object.material = materialScene;
					object.position.set( 0, -150, -150 );
					object.scale.multiplyScalar( 400 );
					scene.add( object );

				} );

				// sphere

				var geo = new THREE.SphereGeometry( 1, 20, 10 );
				sphereMesh = new THREE.Mesh( geo, materialScene );
				sphereMesh.scale.multiplyScalar( 20 );
				scene.add( sphereMesh );

				//

				renderer = new THREE.WebGLRenderer( { antialias: false } );
				renderer.setClearColor( bgColor );
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );
				container.appendChild( renderer.domElement );

				renderer.autoClear = false;
				renderer.sortObjects = false;

				//

				stats = new Stats();
				container.appendChild( stats.dom );

				//

				document.addEventListener( 'mousemove', onDocumentMouseMove, false );
				document.addEventListener( 'touchstart', onDocumentTouchStart, false );
				document.addEventListener( 'touchmove', onDocumentTouchMove, false );

				//

				initPostprocessing();

			}

			//

			function onDocumentMouseMove( event ) {

				mouseX = event.clientX - windowHalfX;
				mouseY = event.clientY - windowHalfY;

			}

			function onDocumentTouchStart( event ) {

				if ( event.touches.length === 1 ) {

					event.preventDefault();

					mouseX = event.touches[ 0 ].pageX - windowHalfX;
					mouseY = event.touches[ 0 ].pageY - windowHalfY;

				}

			}

			function onDocumentTouchMove( event ) {

				if ( event.touches.length === 1 ) {

					event.preventDefault();

					mouseX = event.touches[ 0 ].pageX - windowHalfX;
					mouseY = event.touches[ 0 ].pageY - windowHalfY;

				}

			}

			//

			function initPostprocessing() {

				postprocessing.scene = new THREE.Scene();

				postprocessing.camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2,  window.innerHeight / 2, window.innerHeight / - 2, -10000, 10000 );
				postprocessing.camera.position.z = 100;

				postprocessing.scene.add( postprocessing.camera );

				var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat };
				postprocessing.rtTextureColors = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, pars );

				// Switching the depth formats to luminance from rgb doesn't seem to work. I didn't
				// investigate further for now.
				// pars.format = THREE.LuminanceFormat;

				// I would have this quarter size and use it as one of the ping-pong render
				// targets but the aliasing causes some temporal flickering

				postprocessing.rtTextureDepth = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, pars );

				// Aggressive downsize god-ray ping-pong render targets to minimize cost

				var w = window.innerWidth / 4.0;
				var h = window.innerHeight / 4.0;
				postprocessing.rtTextureGodRays1 = new THREE.WebGLRenderTarget( w, h, pars );
				postprocessing.rtTextureGodRays2 = new THREE.WebGLRenderTarget( w, h, pars );

				// god-ray shaders

				var godraysGenShader = THREE.ShaderGodRays[ "godrays_generate" ];
				postprocessing.godrayGenUniforms = THREE.UniformsUtils.clone( godraysGenShader.uniforms );
				postprocessing.materialGodraysGenerate = new THREE.ShaderMaterial( {

					uniforms: postprocessing.godrayGenUniforms,
					vertexShader: godraysGenShader.vertexShader,
					fragmentShader: godraysGenShader.fragmentShader

				} );

				var godraysCombineShader = THREE.ShaderGodRays[ "godrays_combine" ];
				postprocessing.godrayCombineUniforms = THREE.UniformsUtils.clone( godraysCombineShader.uniforms );
				postprocessing.materialGodraysCombine = new THREE.ShaderMaterial( {

					uniforms: postprocessing.godrayCombineUniforms,
					vertexShader: godraysCombineShader.vertexShader,
					fragmentShader: godraysCombineShader.fragmentShader

				} );

				var godraysFakeSunShader = THREE.ShaderGodRays[ "godrays_fake_sun" ];
				postprocessing.godraysFakeSunUniforms = THREE.UniformsUtils.clone( godraysFakeSunShader.uniforms );
				postprocessing.materialGodraysFakeSun = new THREE.ShaderMaterial( {

					uniforms: postprocessing.godraysFakeSunUniforms,
					vertexShader: godraysFakeSunShader.vertexShader,
					fragmentShader: godraysFakeSunShader.fragmentShader

				} );

				postprocessing.godraysFakeSunUniforms.bgColor.value.setHex( bgColor );
				postprocessing.godraysFakeSunUniforms.sunColor.value.setHex( sunColor );

				postprocessing.godrayCombineUniforms.fGodRayIntensity.value = 0.75;

				postprocessing.quad = new THREE.Mesh(
					new THREE.PlaneBufferGeometry( window.innerWidth, window.innerHeight ),
					postprocessing.materialGodraysGenerate
				);
				postprocessing.quad.position.z = -9900;
				postprocessing.scene.add( postprocessing.quad );

			}

			function animate() {

				requestAnimationFrame( animate, renderer.domElement );

				stats.begin();
				render();
				stats.end();

			}

			function render() {

				var time = Date.now() / 4000;

				sphereMesh.position.x = orbitRadius * Math.cos( time );
				sphereMesh.position.z = orbitRadius * Math.sin( time ) - 100;


				camera.position.x += ( mouseX - camera.position.x ) * 0.036;
				camera.position.y += ( - ( mouseY ) - camera.position.y ) * 0.036;

				camera.lookAt( scene.position );

				if ( postprocessing.enabled ) {

					// Find the screenspace position of the sun

					screenSpacePosition.copy( sunPosition ).project( camera );

					screenSpacePosition.x = ( screenSpacePosition.x + 1 ) / 2;
					screenSpacePosition.y = ( screenSpacePosition.y + 1 ) / 2;

					// Give it to the god-ray and sun shaders

					postprocessing.godrayGenUniforms[ "vSunPositionScreenSpace" ].value.x = screenSpacePosition.x;
					postprocessing.godrayGenUniforms[ "vSunPositionScreenSpace" ].value.y = screenSpacePosition.y;

					postprocessing.godraysFakeSunUniforms[ "vSunPositionScreenSpace" ].value.x = screenSpacePosition.x;
					postprocessing.godraysFakeSunUniforms[ "vSunPositionScreenSpace" ].value.y = screenSpacePosition.y;

					// -- Draw sky and sun --

					// Clear colors and depths, will clear to sky color

					renderer.clearTarget( postprocessing.rtTextureColors, true, true, false );

					// Sun render. Runs a shader that gives a brightness based on the screen
					// space distance to the sun. Not very efficient, so i make a scissor
					// rectangle around the suns position to avoid rendering surrounding pixels.

					var sunsqH = 0.74 * window.innerHeight; // 0.74 depends on extent of sun from shader
					var sunsqW = 0.74 * window.innerHeight; // both depend on height because sun is aspect-corrected

					screenSpacePosition.x *= window.innerWidth;
					screenSpacePosition.y *= window.innerHeight;

					renderer.setScissor( screenSpacePosition.x - sunsqW / 2, screenSpacePosition.y - sunsqH / 2, sunsqW, sunsqH );
					renderer.setScissorTest( true );

					postprocessing.godraysFakeSunUniforms[ "fAspect" ].value = window.innerWidth / window.innerHeight;

					postprocessing.scene.overrideMaterial = postprocessing.materialGodraysFakeSun;
					renderer.render( postprocessing.scene, postprocessing.camera, postprocessing.rtTextureColors );

					renderer.setScissorTest( false );

					// -- Draw scene objects --

					// Colors

					scene.overrideMaterial = null;
					renderer.render( scene, camera, postprocessing.rtTextureColors );

					// Depth

					scene.overrideMaterial = materialDepth;
					renderer.render( scene, camera, postprocessing.rtTextureDepth, true );

					// -- Render god-rays --

					// Maximum length of god-rays (in texture space [0,1]X[0,1])

					var filterLen = 1.0;

					// Samples taken by filter

					var TAPS_PER_PASS = 6.0;

					// Pass order could equivalently be 3,2,1 (instead of 1,2,3), which
					// would start with a small filter support and grow to large. however
					// the large-to-small order produces less objectionable aliasing artifacts that
					// appear as a glimmer along the length of the beams

					// pass 1 - render into first ping-pong target

					var pass = 1.0;
					var stepLen = filterLen * Math.pow( TAPS_PER_PASS, -pass );

					postprocessing.godrayGenUniforms[ "fStepSize" ].value = stepLen;
					postprocessing.godrayGenUniforms[ "tInput" ].value = postprocessing.rtTextureDepth.texture;

					postprocessing.scene.overrideMaterial = postprocessing.materialGodraysGenerate;

					renderer.render( postprocessing.scene, postprocessing.camera, postprocessing.rtTextureGodRays2 );

					// pass 2 - render into second ping-pong target

					pass = 2.0;
					stepLen = filterLen * Math.pow( TAPS_PER_PASS, -pass );

					postprocessing.godrayGenUniforms[ "fStepSize" ].value = stepLen;
					postprocessing.godrayGenUniforms[ "tInput" ].value = postprocessing.rtTextureGodRays2.texture;

					renderer.render( postprocessing.scene, postprocessing.camera, postprocessing.rtTextureGodRays1  );

					// pass 3 - 1st RT

					pass = 3.0;
					stepLen = filterLen * Math.pow( TAPS_PER_PASS, -pass );

					postprocessing.godrayGenUniforms[ "fStepSize" ].value = stepLen;
					postprocessing.godrayGenUniforms[ "tInput" ].value = postprocessing.rtTextureGodRays1.texture;

					renderer.render( postprocessing.scene, postprocessing.camera , postprocessing.rtTextureGodRays2  );

					// final pass - composite god-rays onto colors

					postprocessing.godrayCombineUniforms["tColors"].value = postprocessing.rtTextureColors.texture;
					postprocessing.godrayCombineUniforms["tGodRays"].value = postprocessing.rtTextureGodRays2.texture;

					postprocessing.scene.overrideMaterial = postprocessing.materialGodraysCombine;

					renderer.render( postprocessing.scene, postprocessing.camera );
					postprocessing.scene.overrideMaterial = null;

				} else {

					renderer.clear();
					renderer.render( scene, camera );

				}

			}

		</script>
	</body>
</html>
